// Copyright (c) 2023 Huawei Device Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::event_handler::handler::SharedHandler;
use std::cmp::Ordering;
use std::sync::{Arc, Weak};
use std::time::SystemTime;
use tokio::sync::Notify;

pub(crate) enum Closure<A> {
    Task(BoxedClosure),
    ActorTask(ActorClosure<A>),
}

type BoxedClosure = Box<dyn FnOnce() + Send + 'static>;

type ActorClosure<A> = Box<dyn FnOnce(&mut A) + Send + 'static>;

/// Default type, specify generics in the `EventHandler`.
pub struct DefaultType;

pub(crate) enum EventType<A> {
    Event(u32),
    Task(String, Option<BoxedClosure>),
    ActorTask(String, Option<ActorClosure<A>>),
}

pub(crate) struct Waiter(Arc<Notify>);

impl Waiter {
    fn new() -> Self {
        Self(Arc::new(Notify::new()))
    }

    pub(crate) async fn wait(&self) {
        self.0.notified().await
    }

    fn notify_one(&self) {
        self.0.notify_one()
    }
}

/// Event type can be handled by `EventHandler`。
/// See [`EventHandler`].
///
/// [`EventHandler`]: crate::event_handler::EventHandler
pub struct InnerEvent<A> {
    owner: Option<Weak<SharedHandler>>,
    event: EventType<A>,
    send_time: SystemTime,
    handle_time: SystemTime,
    waiter: Option<Waiter>,
}

unsafe impl<A> Send for InnerEvent<A> {}

impl<A> InnerEvent<A> {
    /// Creates a new `InnerEvent` with user defined event id.
    pub fn new(id: u32) -> InnerEvent<A> {
        InnerEvent {
            owner: None,
            event: EventType::Event(id),
            send_time: SystemTime::UNIX_EPOCH,
            handle_time: SystemTime::UNIX_EPOCH,
            waiter: None,
        }
    }

    pub(crate) fn new_task<F>(name: &str, task: F) -> InnerEvent<A>
    where
        F: FnOnce() + Send + 'static,
    {
        InnerEvent {
            owner: None,
            event: EventType::Task(name.to_string(), Some(Box::new(task))),
            send_time: SystemTime::UNIX_EPOCH,
            handle_time: SystemTime::UNIX_EPOCH,
            waiter: None,
        }
    }

    pub(crate) fn new_actor_task<F>(name: &str, task: F) -> InnerEvent<A>
    where
        F: FnOnce(&mut A) + Send + 'static,
    {
        InnerEvent {
            owner: None,
            event: EventType::ActorTask(name.to_string(), Some(Box::new(task))),
            send_time: SystemTime::UNIX_EPOCH,
            handle_time: SystemTime::UNIX_EPOCH,
            waiter: None,
        }
    }

    /// Gets the event id of the `InnerEvent`.
    pub fn id(&self) -> u32 {
        if let EventType::Event(id) = self.event {
            id
        } else {
            0
        }
    }

    pub(crate) fn set_owner(&mut self, owner: &Arc<SharedHandler>) -> &mut Self {
        self.owner = Some(Arc::downgrade(owner));
        self
    }

    pub(crate) fn owner(&self) -> Option<Arc<SharedHandler>> {
        let weak = self
            .owner
            .as_ref()
            .expect("InnerEvent's owner should always be set after sent");
        weak.upgrade()
    }

    pub(crate) fn set_send_time(&mut self, time: SystemTime) -> &mut Self {
        self.send_time = time;
        self
    }

    pub(crate) fn send_time(&self) -> SystemTime {
        self.send_time
    }

    pub(crate) fn set_handle_time(&mut self, time: SystemTime) -> &mut Self {
        self.handle_time = time;
        self
    }

    pub(crate) fn handle_time(&self) -> SystemTime {
        self.handle_time
    }

    pub(crate) fn task_callback(&mut self) -> Option<Closure<A>> {
        match &mut self.event {
            EventType::Event(_) => None,
            EventType::Task(_, f) => Some(Closure::Task(f.take().expect("No closure exists"))),
            EventType::ActorTask(_, f) => {
                Some(Closure::ActorTask(f.take().expect("No closure exists")))
            }
        }
    }

    pub(crate) fn create_waiter(&mut self) -> Waiter {
        let waiter = Waiter::new();
        self.waiter = Some(Waiter(waiter.0.clone()));
        waiter
    }

    pub(crate) fn cmp(&self, other: &Self) -> Ordering {
        self.handle_time.cmp(&other.handle_time)
    }
}

impl<A> Drop for InnerEvent<A> {
    fn drop(&mut self) {
        if let Some(waiter) = &self.waiter {
            waiter.notify_one()
        }
    }
}
